package client;

import gui.OptionPane;

import java.awt.EventQueue;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.rmi.RemoteException;
import java.util.HashMap;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.UIManager;

import rmi.server.Message;
import rmi.server.Server;
import rmi.server.User;
import toolkit.Debug;
import controller.Controller;

public class Client
{
	private static final String SERVER_HOST_NAME = "localhost";

	private static final int SERVER_SOCKET_PORT = 2005;

	private static final String LOOK_AND_FEEL = "org.jvnet.substance.skin.SubstanceCremeCoffeeLookAndFeel";
	
	private HashMap<String, String> requests = new HashMap<String, String>();
	
	private HashMap<String, Object> responses = new HashMap<String, Object>();
	
	private TrayIcon trayIcon;
	
	public Client()
	{
		// Add a hook for when the application closes.
		Runtime.getRuntime().addShutdownHook(new Thread()
		{
			public void run()
			{
				
			}
		});
	}
	
	public void run()
	{
		requests.put("serverCodebase", "getCodebase");
		requests.put("registryPort", "getRegistryPort");
		
		sendRequests();
		
		try
		{
			String serverCodebase = (String) responses.get("serverCodebase");
			
			int registryPort = (Integer) responses.get("registryPort");
			
			rmi.Client client = new rmi.Client(serverCodebase, registryPort);
			
			// Hack: login with the default user.
			//User user = (User) client.getServerObject("user");
			//Controller.user = user.getDefaultUser();
			
			Controller controller = new controller.Login();
			
			Server server = (Server) client.getServerObject("server");
			controller.setServerRemote(server);
			
			User user = (User) client.getServerObject("user");
			controller.setUserRemote(user);
			
			Message message = (Message) client.getServerObject("message");
			controller.setMessageRemote(message);
			
			controller.show();

			controller.dispatch();
		}
		catch (Exception e)
		{
			closeClient(e);
		}
	}
	
	private void sendRequests()
	{
		try
		{
			// Open a connection to the server.
			Socket socket = new Socket(SERVER_HOST_NAME, SERVER_SOCKET_PORT);
			
			// Create a stream in order to talk to the server (the write half).
			ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
			
			// Write every request to the server.
			for (String request: requests.values())
			{
				out.writeObject(request);
			}
			
			out.flush();
			
			// Now the server is talking back...
			
			// Create a stream in order to hear what the server said back (the read half).
			ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
			
			// The server might have not have responses to all requests.
			boolean responding = true;
			
			// Get all responses of the server.
			for (String key: requests.keySet())
			{
				try
				{
					Object response = (responding
						? in.readObject()
						: null
					);
					
					responses.put(key, response);
				}
				catch (Exception e)
				{
					// The are no more responses to be read or something happens at the server
					// which prevented the server from responding.
					responding = false;
				}
			}
			
			// Now we close all the resources needed to talk to the server.
			// We do not do anything inside the catch blocks, because if an exception is thrown.
			// It will only mean that the file is already closed or cannot be closed, either way
			// there is nothing more we can do. And there is no reason to inform the user of it.
			
			if (out != null)
			{
				try
				{
					out.close();
				}
				catch (Exception e) {}
			}
			
			if (in != null)
			{
				try
				{
					in.close();
				}
				catch (Exception e) {}
			}
			
			if (socket != null)
			{
				try
				{
					socket.close();
				}
				catch (Exception e) {}
			}
		}
		// Something went wrong while talking to the server.
		// Notify the user and close the application.
		catch (Exception e)
		{
			closeClient(e);
		}
	}
	
	private void closeClient(Exception e)
	{
		OptionPane.showError("Geen connectie met de server", "Er kon geen connectie gemaakt worden met de server.\n" +
				"Zonder die connectie kan de applicatie niet verder worden opgestart.\n" +
				"De applicatie wordt afgesloten.");
		
		Debug.log(e);
		
		System.exit(-1);
	}
	
	public static void closeClient()
	{
		int confirm = OptionPane.showConfirm("Are you sure you wish to close the application?");
		
		if (confirm == 0)
		{
			Controller controller = Controller.getLastOpened();
			
			if (controller != null)
			{
				try
				{
					controller.getUserRemote().logout(controller.getUser());
				}
				catch (RemoteException e)
				{
					Debug.log(e);
				}
			}
			
			System.exit(0);
		}
	}
	
	public static void setLookAndFeel()
	{
		setLookAndFeel(LOOK_AND_FEEL);
	}
	
	public static void setLookAndFeel(String lookAndFeel)
	{
		// Use the window skin from the look and feel if provided.
		JFrame.setDefaultLookAndFeelDecorated(true);
		JDialog.setDefaultLookAndFeelDecorated(true);
		
		// Set the appearance.
		try
		{
			UIManager.setLookAndFeel(lookAndFeel);
		}
		catch (Exception e)
		{
			try
			{
				// If the custom look and feel could not be set, we will try to use the system look and feel.
				// The system look and feel is always better than the default of JAVA.
				UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			}
			catch (Exception e1)
			{
				Debug.log(e);
				
				// It is not serious problem because only the graphics are effected by this.
				// So we do not need to notify the user.
			}
		}
	}
	
	public void useTrayIcon()
	{
		if (trayIcon == null)
		{
			trayIcon = new TrayIcon();
		}
	}
	
	public static void main(String[] arguments)
	{
		Client.setLookAndFeel();
		
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				Client client = new Client();
				client.useTrayIcon();
				client.run();
			}
		});
	}
}
